﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Heaven.Texturing.Importers.DirectDraw
{
    /// <summary>
    /// Класс хранящий данные трехмерной текстуры с мипмапами
    /// </summary>
    public class Map3D
    {
        #region Атрибуты

        // Массив всех текстур. От основной к мипмапам по уменьшению размера
        private RawImage[][] textures;

        // Формат пикселей
        private string pixelFormat;

        #endregion

        #region Свойства

        /// <summary>
        /// Формат пикселей
        /// </summary>
        public string PixelFormat
        {
            get { return pixelFormat; }
        }

        /// <summary>
        /// Массив всех текстур. От основной к мипмапам по уменьшению размера
        /// </summary>
        public RawImage[][] Textures
        {
            get { return textures; }
        }

        /// <summary>
        /// Ширина
        /// </summary>
        public int Width
        {
            get { return textures[0][0].Width; }
        }

        /// <summary>
        /// Высота
        /// </summary>
        public int Height
        {
            get { return textures[0][0].Height; }
        }

        /// <summary>
        /// Высота
        /// </summary>
        public int Depth
        {
            get { return textures[0].Length; }
        }

        /// <summary>
        /// Кол-во мипов
        /// </summary>
        public int MipMapCount
        {
            get { return textures.Length-1; }
        }

        #endregion

        #region Инициализация

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="header">Заголовок</param>
        /// <param name="reader">Ридер с данными</param>
        internal Map3D(Header header, BinaryReader reader)
        {
            // Извлекаем нужные данные из заголовка
            int width = header.Width;
            int height = header.Height;
            int depth = header.Depth;
            bool hasMipMaps = (((header.Flags & Header.DDSD_MIPMAPCOUNT)!=0)&&((header.Caps.Caps1 & Header.DDSCAPS_MIPMAP)!=0)&&((header.Caps.Caps1 & Header.DDSCAPS_COMPLEX)!=0));
            int mipMapsCount = 0;
            if (hasMipMaps) mipMapsCount = header.MipMapCount-1;
            textures = new RawImage[mipMapsCount+1][];
            // Определем тип компресии
            if ((header.PixelFormat.Flags & Header.DDPF_FOURCC) == 0)
            {
                // Без компресии
                // Каждый следующий мипмап в два раза меньше предыдущего
                // Определяем формат пикселя
                pixelFormat = "";
                if ((header.PixelFormat.Flags & Header.DDPF_RGB) != 0) pixelFormat = "RGB";
                else if ((header.PixelFormat.Flags & Header.DDPF_LUMINANCE) != 0) pixelFormat = "L";
                else if ((header.PixelFormat.Flags & Header.DDPF_ALPHA) != 0) pixelFormat = "A";
                else if ((header.PixelFormat.Flags & Header.DDPF_YUV) != 0) pixelFormat = "YUV";
                else if (((header.PixelFormat.Flags & Header.DDPF_PALETTEINDEXED1) != 0) || ((header.PixelFormat.Flags & Header.DDPF_PALETTEINDEXED2) != 0) || ((header.PixelFormat.Flags & Header.DDPF_PALETTEINDEXED4) != 0) || ((header.PixelFormat.Flags & Header.DDPF_PALETTEINDEXED8) != 0)) pixelFormat = "P";

                if ((header.PixelFormat.Flags & Header.DDPF_ALPHAPIXELS) != 0) pixelFormat += "A";
                if (pixelFormat.Length == 0) pixelFormat = "Unknown";
                pixelFormat += header.PixelFormat.RgbBitCount;
                // Проверяем наличие питча
                int pitch = header.PitchOrLinearSize;
                if ((header.Flags & Header.DDSD_PITCH) == 0) pitch = width * header.PixelFormat.RgbBitCount / 8;
                // Грузим основной уровень
                textures[0] = new RawImage[depth];
                for (int i = 0; i < depth;i++ )
                {                    
                    textures[0][i] = new RawImage(width, height, reader.ReadBytes(pitch * height));
                }
                // Грузим мипы  
                for(int i=1;i<=mipMapsCount;i++)
                {
                    width /= 2;
                    height /= 2;
                    depth /= 2;
                    if (width == 0) width = 1;
                    if (height == 0) height = 1;
                    if (depth == 0) depth = 1;
                    pitch = width * header.PixelFormat.RgbBitCount / 8;// Могут быть проблемы с двуцветными изображениями(если такие есть)
                    textures[i] = new RawImage[depth];
                    for (int j = 0; j < depth; j++)
                    {
                        textures[i][j] = new RawImage(width, height, reader.ReadBytes(pitch * height));
                    }
                }
            }
            else
            {
                // С компресией
                // Каждый следующий мипмап занимает в два раза меньше места
                // Проверяем наличие линейного размера                
                int minimumSize = 8;
                if (((header.PixelFormat.FourCC >> 24) & 0x000000FF) > (int)'1') minimumSize = 16;
                int linearSize = header.PitchOrLinearSize;
                if ((header.Flags & Header.DDSD_LINEARSIZE) == 0)
                {
                    linearSize = width * height * minimumSize;
                }
                pixelFormat = "";
                pixelFormat += new string((char)((header.PixelFormat.FourCC) & 0x000000FF), 1);
                pixelFormat += new string((char)((header.PixelFormat.FourCC >> 8) & 0x000000FF), 1);
                pixelFormat += new string((char)((header.PixelFormat.FourCC >> 16) & 0x000000FF), 1);
                pixelFormat += new string((char)((header.PixelFormat.FourCC >> 24) & 0x000000FF), 1);
                // Грузим основной уровень
                textures[0] = new RawImage[depth];
                for (int i = 0; i < depth; i++)
                {
                    textures[0][i] = new RawImage(width, height, reader.ReadBytes(linearSize));
                }
                // Грузим мипы  
                for (int i = 1; i <= mipMapsCount; i++)
                {
                    width /= 2;
                    height /= 2;
                    if (width == height) linearSize /= 2;
                    else linearSize = width * height * minimumSize;
                    if (width == 0) width = 1;
                    if (height == 0) height = 1;
                    if (linearSize < minimumSize) linearSize = minimumSize;
                    depth /= 2;
                    if (depth == 0) depth = 1;
                    textures[i] = new RawImage[depth];
                    for (int j = 0; j < depth; j++)
                    {
                        textures[i][j] = new RawImage(width, height, reader.ReadBytes(linearSize));
                    }
                }
            }    
            
        }

        #endregion   
    }
}
